#include "engine.h"
#include "oscillator.h"
#include "project.h"
#include "database/database.h"
#include "database/dbwrapper.h"
#include "filepool.h"
#include "dataroot.h"
#include "datamidi.h"
#include "mixer.h"
#include "sampler/sampler.h"
#include <cmath>
#include "aimididevice.h"

Engine::Engine(DBWrapper & dbwrapper) : data_(), dbwrapper_(dbwrapper),
    sampler_(new Sampler()),
    osc_(new Oscillator)
{
    mixer_ = new Mixer(this);
}

Engine::~Engine(){
    delete sampler_;
    delete osc_;
}

void Engine::setData(PData data){
    data_= data;    
    midi_stack_ = PData (new gl::AudioPorts(0, 2, data->size));
    sampler_->setData(midi_stack_);
 //   osc_->setData(midi_stack_);
}


void Engine::process(){

        static const bool & cycling = true;
        static QMutex & dbMutex = dbwrapper_.mutex();

        static DBWrapper::Rec * locatorRec0 = dbwrapper_.locator(0);
        static DBWrapper::Rec * locatorRec1 = dbwrapper_.locator(1);
        static DBWrapper::Rec * lastRec = dbwrapper_.root();
        static gl::TicksTime curTime = 0;
        static gl::TicksTime nextTime = 0;
        static gl::TicksTime restOfTime;
        static size_t & bufSize = data_->size;
        static FilePool filePool;

        restOfTime = bufSize;

        gl::PcmSampleType * bufferL = data_->outputs[1];
        gl::PcmSampleType * bufferR = data_->outputs[0];

        Q_UNUSED (bufferR)

        static AIMidiDevice::MidiDataList midi_data;
        midi_data.clear();
        sampler_->setMidiData(midi_data);

        while (restOfTime > 0){
            nextTime = cycling ? ( locatorRec1->index < (curTime + restOfTime) &&
                                   locatorRec1->index >= curTime ?
                                       locatorRec1->index :
                                       curTime + restOfTime
                                       ) :
                                 curTime + restOfTime;
            restOfTime -= (nextTime - curTime);
            dbMutex.lock();
            while (lastRec->next != NULL){
                if (lastRec->next->index >= nextTime) break;
                if (lastRec->next->index >= curTime && lastRec->next->index < nextTime){
                    lastRec = lastRec->next;
                    if (lastRec->type == DBWrapper::MIDI) {
                            midi_data.append(AIMidiDevice::TimedMidiData(
                              static_cast<DataMidi*>(lastRec->data),
                              lastRec->index - curTime));
                    }
                }
            }
            if (cycling && nextTime >= locatorRec1->index){
                lastRec = locatorRec0;
                curTime = locatorRec0->index;
            } else { curTime = nextTime;}
            dbMutex.unlock();
        }

        sampler_->process();

//    osc_->process();
    memcpy(bufferL,
           midi_stack_->outputs[0],
           sizeof(gl::PcmSampleType)* midi_stack_->size);
    memcpy(bufferR,
           midi_stack_->outputs[0],
           sizeof(gl::PcmSampleType)* midi_stack_->size);
}
